package main

import (
	"encoding/binary"
	"fmt"
	"sync"
)

type crc struct {
	once  sync.Once
	table []uint16
}

type FCURealData struct{
	Addr 				byte		`json:"Addr"`
	DeviceType  		string		`json:"DeviceType"`
	SVer 				string		`json:"SVer"`
	SerialNumber    	string		`json:"SerialNumber"`
	RTC             	string		`json:"RTC"`
	RoomTemp        	string		`json:"RoomTemp"`
	RoomHumi        	string		`json:"RoomHumi"`
	AirStatus       	byte		`json:"AirStatus"`
	RelayStatus     	byte		`json:"RelayStatus"`
	ModeStatus      	byte		`json:"ModeStatus"`
	SeaconStatus   	 	byte		`json:"SeaconStatus"`
	CurSetTemp      	byte		`json:"CurSetTemp"`
	LockStatus      	byte		`json:"LockStatus"`
	EnergySavingStatus 	byte		`json:"EnergySavingStatus"`
	RSSI            	byte		`json:"RSSI"`
	BLKStatus       	byte		`json:"BLKStatus"`
	ErrorCode       	uint16		`json:"ErrorCode"`
	TotalTime       	uint32		`json:"TotalTime"`
	LowTotalTime    	uint32		`json:"LowTotalTime"`
	MiddleTotalTime 	uint32		`json:"MiddleTotalTime"`
	HighTotalTime   	uint32		`json:"HighTotalTime"`
}

var (
	crcTb crc
	fcuRealData FCURealData
)



// initTable 初始化表
func (c *crc) initTable() {
	crcPoly16 := uint16(0xa001)
	c.table = make([]uint16, 256)

	for i := uint16(0); i < 256; i++ {
		crc := uint16(0)
		b := i

		for j := uint16(0); j < 8; j++ {
			if ((crc ^ b) & 0x0001) > 0 {
				crc = (crc >> 1) ^ crcPoly16
			} else {
				crc = crc >> 1
			}
			b = b >> 1
		}
		c.table[i] = crc
	}
}

func crc16(bs []byte) uint16 {
	crcTb.once.Do(crcTb.initTable)

	val := uint16(0xFFFF)
	for _, v := range bs {
		val = (val >> 8) ^ crcTb.table[(val^uint16(v))&0x00FF]
	}
	return val
}


func FCUGetRealData(addr byte) bool {
	requestAdu := make([]byte,0)

	requestAdu = append(requestAdu,addr)
	requestAdu = append(requestAdu,0x03)
	requestAdu = append(requestAdu,0x00,0x30)
	requestAdu = append(requestAdu,0x00,0x23)

	checksum := crc16(requestAdu)
	requestAdu = append(requestAdu,byte(checksum),byte(checksum>>8))

	status,ackAdu := threadModuleWriteData(addr,requestAdu)
	if status == true{
		if len(ackAdu) < 4{
			return false
		}
		crc := crc16(ackAdu[0:len(ackAdu)-2])
		expect := binary.LittleEndian.Uint16(ackAdu[len(ackAdu)-2:])
		if crc != expect{
			return false
		}
		if ackAdu[0] != addr{
			return false
		}
		if ackAdu[1] != 0x03{
			return false
		}
		if ackAdu[2] != 70{
			return false
		}
		var aduIndex byte = 3

		fcuRealData.Addr = ackAdu[aduIndex+1]
		aduIndex += 2

		deviceType := binary.BigEndian.Uint16(ackAdu[aduIndex:])
		fcuRealData.DeviceType = fmt.Sprintf("%04X",deviceType)
		aduIndex += 2

		fcuRealData.SVer = fmt.Sprintf("V%02X.%02X",ackAdu[aduIndex],ackAdu[aduIndex+1])
		aduIndex += 2

		var serialString string = ""
		for _,v := range ackAdu[aduIndex:aduIndex+6]{
			serialString += fmt.Sprintf("%02X",v)
		}
		fcuRealData.SerialNumber = serialString
		aduIndex += 6

		var rtcString string = ""
		for _,v := range ackAdu[aduIndex:aduIndex+6]{
			rtcString += fmt.Sprintf("%02X",v)
		}
		fcuRealData.RTC = rtcString
		aduIndex += 6

		roomTemp := binary.BigEndian.Uint16(ackAdu[aduIndex:])
		fcuRealData.RoomTemp = fmt.Sprintf("%.1f",float32(roomTemp/10.0))
		aduIndex += 2

		roomHumi := binary.BigEndian.Uint16(ackAdu[aduIndex:])
		fcuRealData.RoomHumi = fmt.Sprintf("%d",roomHumi)
		aduIndex += 2

		fcuRealData.AirStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.RelayStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.ModeStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.SeaconStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.CurSetTemp = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.LockStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.EnergySavingStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.RSSI = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.BLKStatus = ackAdu[aduIndex+1]
		aduIndex += 2

		fcuRealData.ErrorCode = binary.BigEndian.Uint16(ackAdu[aduIndex:])
		aduIndex += 2

		fcuRealData.TotalTime = binary.BigEndian.Uint32(ackAdu[aduIndex:])
		aduIndex += 4
	}

	return true
}

func FCUSetRemoteCmd(addr byte,cmd uint16) bool {

	requestAdu := make([]byte,0)

	requestAdu = append(requestAdu,addr)
	requestAdu = append(requestAdu,0x10)
	requestAdu = append(requestAdu,0x00,0x88)
	requestAdu = append(requestAdu,0x00,0x01)
	requestAdu = append(requestAdu,0x02)
	requestAdu = append(requestAdu,byte(cmd>>8),byte(cmd))

	checksum := crc16(requestAdu)
	requestAdu = append(requestAdu,byte(checksum),byte(checksum>>8))

	status,ackAdu := threadModuleWriteData(addr,requestAdu)
	if status == true{
		if len(ackAdu) < 4{
			return false
		}
		crc := crc16(ackAdu[0:len(ackAdu)-2])
		expect := binary.LittleEndian.Uint16(ackAdu[len(ackAdu)-2:])
		if crc != expect{
			return false
		}
		if ackAdu[0] != addr{
			return false
		}
		if ackAdu[1] != 0x10{
			return false
		}
		return true
	}else{
		return false
	}
}

